#include <iostream>
#include <fstream>



#include "../invertedindex.h"
#include "../treehandler.h"
#include "../frontcodingmanager.h"
#include "../wordcontainer.h"
#include "../invertedindexdata.h"

#include "../document.h"
#include "../documentscontainer.h"
#include "../streamhandler.h"

using namespace invertedindex;

typedef std::vector<Word*> Wrds;
typedef std::set<int> IntSet;

void addDocumentsRandomIID(InvertedIndexData& iid);
void generateRandomIID(InvertedIndexData& iid);
void generateIID(InvertedIndexData& iid, Word& wr);
unsigned int loadFromFile(Wrds& wrds);
void testStreamHandler();
void testTreeHandler();
void testInvertedIndex();
void test();



int main(int argc, char** argv){
	//testTreeHandler();

	//testStreamHandler();

	//testInvertedIndex();

	test();

	return 0;
}



void testTreeHandler(){


	//-------------------------  Defino Palabras  -------------------------

	//Word1
	unsigned int wordLength1=9;
	char* word1=new char[9];
	memcpy(word1,"HolaMundo",9);
	unsigned int wordId1=1;
	//Word2
	unsigned int wordLength2=9;
	char* word2=new char[9];
	memcpy(word2,"HolaMunda",9);
	unsigned int wordId2=2;
	//Word3
	unsigned int wordLength3=8;
	char* word3=new char[8];
	memcpy(word3,"HolaFFFF",8);
	unsigned int wordId3=3;
	//Word4
	unsigned int wordLength4=3;
	char* word4=new char[3];
	memcpy(word4,"Hol",3);
	unsigned int wordId4=4;
	//Word5
	unsigned int wordLength5=8;
	char* word5=new char[8];
	memcpy(word5,"GGGGGGGG",8);
	unsigned int wordId5=5;
	//Word6
	unsigned int wordLength6=8;
	char* word6=new char[8];
	memcpy(word6,"GGGGasdr",8);
	unsigned int wordId6=6;




	//-------------------------  Test WordContainer  -------------------------
	BlockPointer* bpAux;
	WordContainer wrd1(wordLength1,word1,wordId1);
	bpAux = new BlockPointer(1,1);
	wrd1.setBP(bpAux);
	delete bpAux;
	WordContainer wrd2(wordLength2,word2,wordId2);
	bpAux = new BlockPointer(1,2);
	wrd2.setBP(bpAux);
	delete bpAux;
	WordContainer wrd3(wordLength3,word3,wordId3);
	bpAux = new BlockPointer(2,3);
	wrd3.setBP(bpAux);
	delete bpAux;
	WordContainer wrd4(wordLength4,word4,wordId4);
	bpAux = new BlockPointer(2,4);
	wrd4.setBP(bpAux);
	delete bpAux;
	WordContainer wrd5(wordLength5,word5,wordId5);
	bpAux = new BlockPointer(4,5);
	wrd5.setBP(bpAux);
	delete bpAux;
	WordContainer wrd6(wordLength6,word6,wordId6);
	bpAux = new BlockPointer(4,6);
	wrd6.setBP(bpAux);
	delete bpAux;

	if(wrd2.comparate(wrd1)<0)
		std::cout<<"p1<p2"<<std::endl;
	wrd4.toOstream(std::cout);





	//-------------------------  Test frontcodingmanager  -------------------------

	FrontCodingManager fcm(wrd1,0,NULL);

	fcm.insertWord(wrd3);
	fcm.insertWord(wrd4);

	if(fcm.insertWord(wrd2)==true){
		std::cout<<"insertoWord2Correctamente: "<<fcm.getWordCount()<<std::endl;
		WordContainer wc(wordLength4,word4,0);
		if(fcm.searchWord(wc)==true);
			wc.toOstream(std::cout);
	}

	std::pair<unsigned int, char*> fc=fcm.getFrontCoding();
	{
		unsigned int nWs = 3;
		unsigned short int numberOfDifferentLetters;
		unsigned short int numberOfRepetedLetters;
		unsigned int idKey;
		unsigned int wordLength;
		char* newWord;		

		char* temp1 = fc.second;
		char* temp2 = fc.second+nWs*(2*sizeof(unsigned short int)+2*sizeof(unsigned int));
	
		unsigned int i;
		for(i=0; i< nWs; i++){
			memcpy(&numberOfDifferentLetters, temp1, sizeof(unsigned short int));
			temp1+=sizeof(unsigned short int);	
			memcpy(&numberOfRepetedLetters, temp1, sizeof(unsigned short int));
			temp1+=sizeof(unsigned short int);
			memcpy(&idKey, temp1, sizeof(unsigned int));
			temp1+=sizeof(unsigned int);

			BlockPointer blockPointer(0,0);
			temp1=blockPointer.toFilePointer(temp1);	

			wordLength=numberOfDifferentLetters;
			newWord=new char[wordLength];
			memcpy(newWord, temp2, numberOfDifferentLetters);
			temp2+=numberOfDifferentLetters;
	

			std::cout << "\nWordId: " << idKey << std::endl;
			std::cout << "numberOfDif: " << numberOfDifferentLetters << std::endl;	
			std::cout << "numberOfEq: " << numberOfRepetedLetters << std::endl;
			blockPointer.toOstream(std::cout);
			std::cout << "Word: ";
			char* temp=newWord;
			unsigned int i;
			for(i=0; i<wordLength; i++){
				std::cout<<*temp;
				temp++;
			}
			std::cout << std::endl;
			delete[] newWord;
		}
	}
	std::cout<<"\n";
	FrontCodingManager fcmB(wrd4,3,fc.second);
	WordContainer wcV(wordLength4,word4,0);
	if(fcmB.searchWord(wcV)==true);
			wcV.toOstream(std::cout);
	delete[] fc.second;

	FrontCodingManager tD1(wrd1,0,NULL);
	tD1.insertWord(wrd2);
	tD1.insertWord(wrd3);
	tD1.insertWord(wrd4);
	tD1.insertWord(wrd5);
	FrontCodingManager* tD2 = tD1.divide();

	tD1.toOstream(std::cout);
	tD2->toOstream(std::cout);
	delete tD2;





	//-------------------------  Test tree Handler  -------------------------

	TreeHandler th("conf/");
	if(th.insertWord(wrd1)==true)
		std::cout<<"Exito\n";
	//th.printTree();
	th.insertWord(wrd2);
	//th.printTree();
	th.insertWord(wrd3);
	//th.printTree();
	th.insertWord(wrd4);
	//th.printTree();
	th.insertWord(wrd5);
	//th.printTree();
	th.insertWord(wrd6);
	th.toOstream(std::cout);

	//Test Search
	WordContainer wrdSrc(wordLength4,word4,0);
	if(th.getWord(wrdSrc)==true)
		std::cout<<"Exito\n";
	wrdSrc.toOstream(std::cout);	

	//Test modifyOffset
	WordContainer wrdMod(wordLength4,word4,0);
	//wrdMod.setOffset(55);
	th.modifyWordBlockPointer(wrdMod);  //Modifico offset palabra
	WordContainer wrdSrc2(wordLength4,word4,0);
	th.getWord(wrdSrc2);		//Busco palabra pare ver si se modifico
	std::cout<<"palabra modificada: \n";
	wrdSrc2.toOstream(std::cout);	

	delete[] word1;
	delete[] word2;
	delete[] word3;
	delete[] word4;
	delete[] word5;
	delete[] word6;

}




void testStreamHandler(){
	
	//Defino documetos

	Document doc1(1);
	doc1.insertPosition(7);
	doc1.insertPosition(14);
	doc1.insertPosition(28);
	doc1.insertPosition(42);

	Document doc2(2);
	doc2.insertPosition(6);
	doc2.insertPosition(12);
	doc2.insertPosition(24);

	
	//Defino palabras

	//Word1
	unsigned int wordLength1=9;
	char* word1=new char[9];
	memcpy(word1,"HolaMundo",9);
	unsigned int wordId1=1;
	WordContainer wrd1(wordLength1,word1,wordId1);


	//Word2
	unsigned int wordLength2=9;
	char* word2=new char[9];
	memcpy(word2,"HolaMunda",9);
	unsigned int wordId2=2;
	WordContainer wrd2(wordLength2,word2,wordId2);



	//-------------------------  Test Documents container  -------------------------

	DocumentsContainer dC;

	dC.insertDocument(doc1);
	dC.insertDocument(doc2);
	dC.toOstream(std::cout);


	//-------------------------  Test stream handler  -------------------------
	
	StreamRegister sr(2);
	sr.insertDocument(doc1);
	sr.getSize();

	//pruebo escritura
	StreamHandler sh;

	sh.insertDocuments(wrd1, dC);
	wrd1.toOstream(std::cout);

	sh.toOstream(std::cout);

	//pruebo lectura
	DocumentsContainer dC2;
	sh.searchDocuments(wrd1, dC2);
	dC2.toOstream(std::cout);

	dC2.removeDocument(doc2);

	sh.updateDocuments(wrd1,dC2);

	sh.toOstream(std::cout);

	delete[] word1;
	delete[] word2;
}


void testInvertedIndex(){

	//Defino documetos
	Document doc1(1);
	doc1.insertPosition(7);
	doc1.insertPosition(14);
	doc1.insertPosition(28);
	doc1.insertPosition(42);
	Document doc2(2);
	doc2.insertPosition(6);
	doc2.insertPosition(12);
	doc2.insertPosition(24);


	//Defino invertedindexdata
	//Word1
	unsigned int wordLength1=9;
	char* word1=new char[9];
	memcpy(word1,"HolaMundo",9);
	unsigned int wordId1=1;
	InvertedIndexData iid(wordLength1,word1,wordId1);
	//Word2
	unsigned int wordLength2=9;
	char* word2=new char[9];
	memcpy(word2,"HolaMunda",9);
	unsigned int wordId2=2;
	InvertedIndexData iid2(wordLength2,word2,wordId2);
	iid2.insertDocument(doc1);

	//creo inverted index
	InvertedIndex ii;

	//Inserto invertedIndexData en InvertedIndex
	ii.insert(iid);
	ii.THtoOstream(std::cout);
	ii.insert(iid2);
	ii.THtoOstream(std::cout);
	ii.SHtoOstream(std::cout);
	std::cout<<"--------------------------1\n";
	iid.insertDocument(doc1);
	iid.insertDocument(doc2);
	ii.update(iid);
	std::cout<<"--------------------------2\n";
	ii.THtoOstream(std::cout);
	ii.SHtoOstream(std::cout);
	

	//Pruebo busqueda
	InvertedIndexData iid3(wordLength1,word1,0);
	ii.search(iid3);
	iid3.toOstream(std::cout);

	delete[] word1;
	delete[] word2;
}

void test(){
	static const unsigned int ARRAY_SIZE = 20;
	static const unsigned int ARRAY_SIZE_UPDATE = 10;

	InvertedIndexData v_iid[ARRAY_SIZE];
	Wrds wrds;

	IntSet is;
	IntSet::iterator it;

	srand(time(0));


	//-------------  Elegir uno de los dos metodos siguientes para cargar vector ------------ 
	//De las palabras del archivo cargo N aleatorias en InvertedIndexDatas
	unsigned int registersCount=loadFromFile(wrds);
	unsigned int random=0;
	for(unsigned int i=0; i<ARRAY_SIZE; i++){
		//Cargo una palabra aleatoria en vector
		random = (rand()%registersCount); 
		it=is.find(random);
		while(it!=is.end()){
			random = (rand()%registersCount); 
			it=is.find(random);
		}
		is.insert(random);
		generateIID(v_iid[i],*(wrds[random]));
	}

	//Cargo palabras aleatorias al vector
	/*for(unsigned int i=0; i<ARRAY_SIZE; i++){
		generateRandomIID(v_iid[i]);
	}*/

	//------------------------------ Fin eleccion ------------------------------ 

	std::ofstream out1;
	out1.open("./data/cargaInicial.txt", std::ios::out);
	for(unsigned int h=0; h<ARRAY_SIZE; h++){
		v_iid[h].toOstream(out1);
	}
	out1.close();

	//creo inverted index
	InvertedIndex ii;

	//Cargo palabras a inverted index
	unsigned int a=0;
	for(unsigned int h=0; h<ARRAY_SIZE; h++){
		ii.insert(v_iid[h]);
		std::cout<<".";
		a++;
		if(a==20){
			std::cout.flush();
			a=0;
		}
	}
	std::cout<<std::endl;
	//Imprimo arbol a disco
	out1.open("./data/TreeInicial.txt", std::ios::out);
	ii.THtoOstream(out1);
	out1.close();

	//Imprimo stream file a disco
	out1.open("./data/StreamInicial.txt", std::ios::out);
	ii.SHtoOstream(out1);
	out1.close();

	std::cout<<std::endl;
	std::cout<<"-------------------------------------\n";

	out1.open("./data/Nodo0.txt", std::ios::out);
	ii.printTreeBlock(out1, 0);
	out1.close();
/*
	//Controlo que se puedan recuperar todos los registros ingresados
	bool control=true;
	a=0;
	for(unsigned int h=0; h<ARRAY_SIZE; h++){
		Word* wr = v_iid[h].getWordObject();
		InvertedIndexData iidTst(0, NULL, 0);
		iidTst.setWord(*wr);
		delete wr;
		ii.search(iidTst);
		if(!(iidTst==v_iid[h])){
			std::cout<<"INCORRECTO_"<<h<<std::endl;
			iidTst.toOstream(std::cout);
			std::cout<<"INCORRECTO Original"<<std::endl;
			v_iid[h].toOstream(std::cout);
			control=false;
		}
		std::cout<<"+";
		a++;
		if(a==20){
			std::cout.flush();
			a=0;
		}
	}
	std::cout<<std::endl;
	if(control)
		std::cout<<"SE RECUPERARON REGISTROS CORRECTAMENTE"<<std::endl;








	//------------------------------   Hago algunos update   ------------------------------
 
	//Cargo palabras aleatorias al vector
	for(unsigned int i=0; i<ARRAY_SIZE_UPDATE; i++){
		addDocumentsRandomIID(v_iid[i]);
	}

	//Cargo palabras a inverted index
	a=0;
	for(unsigned int h=0; h<ARRAY_SIZE_UPDATE; h++){
		ii.update(v_iid[h]);
		std::cout<<".";
		a++;
		if(a==20){
			std::cout.flush();
			a=0;
		}
	}

	//Controlo que se puedan recuperar todos los registros ingresados
	control=true;
	a=0;
	for(unsigned int h=0; h<ARRAY_SIZE_UPDATE; h++){
		Word* wr = v_iid[h].getWordObject();
		InvertedIndexData iidTst(0, NULL, 0);
		iidTst.setWord(*wr);
		delete wr;
		ii.search(iidTst);
		if(!(iidTst==v_iid[h])){
			std::cout<<"INCORRECTO_"<<h<<std::endl;
			iidTst.toOstream(std::cout);
			std::cout<<"INCORRECTO Original"<<std::endl;
			v_iid[h].toOstream(std::cout);
			control=false;
		}
		std::cout<<"+";
		a++;
		if(a==20){
			std::cout.flush();
			a=0;
		}
	}
	std::cout<<std::endl;
	if(control)
		std::cout<<"SE RECUPERARON REGISTROS CORRECTAMENTE"<<std::endl;





	//------------------------------  Controlo que exist funcione  ------------------------------ 
	control=true;
	a=0;
	for(unsigned int h=0; h<ARRAY_SIZE_UPDATE; h++){
		Word* wr = v_iid[h].getWordObject();
		wr->setWordId(0);
		InvertedIndexData iidTst(0, NULL, 0);
		iidTst.setWord(*wr);
		delete wr;
		ii.exist(iidTst);
		if(!(iidTst.getWordId()==v_iid[h].getWordId())){
			std::cout<<"INCORRECTO_"<<h<<std::endl;
			iidTst.toOstream(std::cout);
			std::cout<<"INCORRECTO Original"<<std::endl;
			v_iid[h].toOstream(std::cout);
			control=false;
		}
		std::cout<<"+";
		a++;
		if(a==20){
			std::cout.flush();
			a=0;
		}
	}
	std::cout<<std::endl;
	if(control)
		std::cout<<"SE RECUPERARON REGISTROS CORRECTAMENTE"<<std::endl;
*/
	//Limpio lista palabras
	for(Wrds::iterator it=wrds.begin();it!=wrds.end();it++){
		delete(*it);
	}
	wrds.clear();
	
}

void generateIID(InvertedIndexData& iid, Word& wr){
	//Guardo palabra
	iid.setWord(wr);
	//Genero documentos
	unsigned int docCount = (rand()%9)+1;
	unsigned int docOccurCount;
	unsigned int random;
	random=rand()%1000000;
	iid.setWordId(random);
	for(unsigned int i=0; i<docCount;i++){
		random=rand()%10000+1;
		Document doc1(random);
		docOccurCount=rand()%4+1;
		for(unsigned int j=0;j<docOccurCount;j++){
			random=rand()%500+1;
			doc1.insertPosition(random);
		}
		iid.insertDocument(doc1);
	}

}



unsigned int loadFromFile(Wrds& wrds){
	unsigned int wordCount=0;
	std::ifstream in;
	in.open("./inputData/palabras.txt");
	if ( ! in ) {
		return 0;
	}
	std::string str;
	while (in){
		std::getline(in,str);
		if(str.size()>=2){
			Word* w = new Word(str.size(), str.c_str(), 0); 
			wrds.push_back(w);
			wordCount++;
		}
	}

	in.close();
	
	/*for(unsigned int h=0; h<wordCount; h++){
		wrds[h]->toOstream(std::cout);
	}*/

	return wordCount;
}

std::string generateRandomWord(unsigned int length){
	std::string ret;
	int random_integer;
	for(unsigned int i=0; i<length; i++){
		random_integer = (rand()%25)+97; 
		ret+=(char)random_integer;
	}
	return ret;
}

void generateRandomIID(InvertedIndexData& iid){
	unsigned int random_lenght = (rand()%5)+4;
	std::string str=generateRandomWord(random_lenght);

	Word wr(str.size(), str.c_str(), 0); 

	//Guardo palabra
	iid.setWord(wr);
	//Genero documentos
	unsigned int docCount = (rand()%9)+1;
	unsigned int docOccurCount;
	unsigned int random;
	random=rand()%1000000;
	iid.setWordId(random);
	for(unsigned int i=0; i<docCount;i++){
		random=rand()%10000+1;
		Document doc1(random);
		docOccurCount=rand()%4+1;
		for(unsigned int j=0;j<docOccurCount;j++){
			random=rand()%500+1;
			doc1.insertPosition(random);
		}
		iid.insertDocument(doc1);
	}
}

void addDocumentsRandomIID(InvertedIndexData& iid){

	//Genero documentos
	unsigned int docCount = (rand()%9)+1;
	unsigned int docOccurCount;
	unsigned int random;
	for(unsigned int i=0; i<docCount;i++){
		random=rand()%10000+1;
		Document doc1(random);
		docOccurCount=rand()%4+1;
		for(unsigned int j=0;j<docOccurCount;j++){
			random=rand()%500+1;
			doc1.insertPosition(random);
		}
		iid.insertDocument(doc1);
	}
}


